
package suncertify.db;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author jshah
 * LockManager maintains an instance of LockRegister and provides API's for locking, unlocking record .
 */
public class LockManager {

    /**
     * holds the <code>LockRegister</code> Instance passed to the LockManager
     */
    LockRegister lockregister;

    /**
     *
     * @param lockregister  A <code>LockRegister</code> object which contains the <code>Lock</code> objects for all Records
     */
    LockManager(LockRegister lockregister) {
        this.lockregister = lockregister;
    }

    /**
     * Locks a record in lock register and returns the 'LockRegister.Lock; object
     * if the record is unlocked it sets the key code for that lock otherwise matches the
     * given keyCode and returns the record if keyCode does not match return null"for now"
     * @param RecNo current record no. on which the lock is desired
     * @param keyCode keyCode supplied for that record to unlock that record
     * @return Returns a lock object if successfully locked otherwise returns a null
     */
    long lock(int RecNo) {

        // check if lock is unlocked
        LockRegister.Lock tempLock = this.lockregister.register.get(RecNo);
        long              cookie;

        synchronized (tempLock) {
            if (tempLock.isLocked()) {
                try {
                    System.out.println(Thread.currentThread().getName() + "WAITING:");
                    tempLock.wait();
                    System.out.println(Thread.currentThread().getName() + "RELEASED:");
                } catch (InterruptedException ex) {
                    Logger.getLogger(LockManager.class.getName()).log(Level.SEVERE, "LOCK THREAD INTERRUPTED", ex);
                }
            } else {
                Logger.getLogger(LockManager.class.getName()).log(Level.INFO, "{0}GOT IT UNLOCKED:", Thread.currentThread().getName());
            }

            {
                cookie = (new java.util.Random()).nextLong();
                tempLock.setKeyCode(cookie);
                tempLock.setLocked(true);
            }
        }

        return cookie;
    }

    /**
     * Unlocks a record by if the given KeyCode matches the given keyCode and returns true if unloced succesfully
     * @param recNo The record which is supposed to be unlocked
     * @param keyCode keyCode of the locked object
     * @return returns true to show success
     */
    void unlock(int recNo, long keyCode) throws SecurityException {
        LockRegister.Lock currLock = this.lockregister.register.get(recNo);
        synchronized (currLock) {
            if (isLocked(recNo) && (currLock.getKeyCode() == keyCode)) {
                currLock.setKeyCode(-1);
                currLock.setLocked(false);
                currLock.notify();
            } else if (!isLocked(recNo)) {
                Logger.getLogger(LockManager.class.getName()).log(Level.INFO, "Object Already Unlocked");
                currLock.notify();
            } else {
                Logger.getLogger(LockManager.class.getName()).log(Level.SEVERE, "{0}NOT YOUR TURN THROWING SECURITY EXCEPION",Thread.currentThread().getName());
                throw new SecurityException("Unauthorized client tried to unlock the record");
            }
            System.out.println(Thread.currentThread().getName() + "DONE:");
        }
    }

    /**
     * Returns true if lock object is locked
     * @param RecNo the record no. to be checked
     * @return returns true if record is locked
     */
    boolean isLocked(int RecNo) {
        LockRegister.Lock currLock = this.lockregister.register.get(RecNo);

        if (currLock.getKeyCode() != -1) {
            return true;
        }

        return false;
    }

    /**
     * If a new Record is created it adds the new Record to the Lock Register
     * @param RecNo     The Record index for the new Record to be added to LockRegister
     */
    void addLockToRegister(int RecNo) {
        this.lockregister.addLockToRegister(RecNo);
    }
}


